#include "ContnavLogger.h"

/**
 *
 * using namespace yazi::utility;
 * using namespace contnav::log;
 *  #include "ContnavLogger.h"
 *  // init ContnavLogger
    ContnavLogger::instance()->open(m_root_path + "/log/main.log");
 */

using namespace contnav::log;
using namespace std;


const char* ContnavLogger::s_level[LEVEL_COUNT] =
        {
                "DEBUG",
                "INFO",
                "WARN",
                "ERROR",
                "FATAL"
        };

ContnavLogger *ContnavLogger::m_instance = NULL;

ContnavLogger::ContnavLogger() : m_max(0), m_len(0), m_level(DEBUG)
{
}

ContnavLogger::~ContnavLogger()
{
    close();
}

ContnavLogger* ContnavLogger::instance()
{
    if (m_instance == NULL)
        m_instance = new ContnavLogger();
    return m_instance;
}

void ContnavLogger::open(const string &filename)
{

    try{
        // close();
        m_filename = filename;
        m_fout.open(filename.c_str(), ios::app);
        if (m_fout.fail())
        {
            throw std::logic_error("open log file failed: " + filename);
        }
        m_fout.seekp(0, ios::end);//获取内容的长度
        m_len = m_fout.tellp();//调用这个方法获取文件内容的长度
    }catch (exception e){
        //互锁
        m_.unlock();
        //发送指令
        std::cout << "log 模块 异常 open error" << std::endl;
    }


}

void ContnavLogger::close()
{
    m_fout.close();
}


void ContnavLogger::log(Level level, const char* file, int line, const char* format, ...)
{
    //互锁
    m_.lock();

    try{
        //判断当前的日志级别，不做任何记录；
        if (m_level > level)
        {
            //互锁
            m_.unlock();
            return;
        }

        if (m_fout.fail())
        {
            //互锁
            m_.unlock();
            throw std::logic_error("open log file failed: " + m_filename);
        }

        time_t ticks = time(NULL);
        struct tm* ptm = localtime(&ticks);
        char timestamp[32];
        memset(timestamp, 0, sizeof(timestamp));
        strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", ptm);

        int len = 0;
        const char * fmt = "%s %s %s:%d ";
        len = snprintf(NULL, 0, fmt, timestamp, s_level[level], file, line);//获取到当前日志的长度
        if (len > 0)
        {
            char * buffer = new char[len + 1];
            snprintf(buffer, len + 1, fmt, timestamp, s_level[level], file, line);
            buffer[len] = 0;
            m_fout << buffer;
            delete [] buffer;
            m_len += len;//获取到当前日志的长度 累计起来
        }

        va_list arg_ptr;
        va_start(arg_ptr, format);
        len = vsnprintf(NULL, 0, format, arg_ptr);
        va_end(arg_ptr);
        if (len > 0)
        {
            char * content = new char[len + 1];
            va_start(arg_ptr, format);
            vsnprintf(content, len + 1, format, arg_ptr);
            va_end(arg_ptr);
            content[len] = 0;
            m_fout << content;
            delete [] content; //动态的数组需要删除
            m_len += len;//获取到当前日志的长度 累计起来
        }

        m_fout << "\n";
        m_fout.flush();

        if (m_max > 0 && m_len >= m_max) //判断日志长度是否大于最大的长度
        {
            rotate();//日志翻滚
        }
    }catch (exception e){
        //互锁
        m_.unlock();
        //发送指令
        std::cout << "log 模块 异常 error" << std::endl;
    }
    m_.unlock();
}


void ContnavLogger::max(int bytes)
{
    m_max = bytes;
}

void ContnavLogger::level(int level)
{
    m_level = level;

}

void ContnavLogger::rotate()
{
    close();//首先把之前打开的文件关闭掉

    //调用当前的时间戳
    time_t ticks = time(NULL);
    struct tm* ptm = localtime(&ticks);
    char timestamp[32];
    memset(timestamp, 0, sizeof(timestamp));
    strftime(timestamp, sizeof(timestamp), ".%Y-%m-%d_%H-%M-%S", ptm);


    string filename = m_filename + timestamp;
    if (rename(m_filename.c_str(), filename.c_str()) != 0) //rename备份到新的文件中
    {
        //互锁
        m_.unlock();
        throw std::logic_error("rename log file failed: " + string(strerror(errno)));
    }
    open(m_filename);//重新打开新的文件进行日志写入
}